package com.sz.biz.common;

import com.sz.common.base.utils.DateUtils;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

/**
 * Package: com.sz.biz.common <br>
 * Function: TODO: ADD FUNCTION <br>
 * Author: Charles. <br>
 * Date: 2017-03-21 14:42:00.
 */
public class TimelineAlgorithm {
    private Date finalDate;
    private boolean allowGap = false;

    /**
     * @param allowGap
     */
    public TimelineAlgorithm(boolean allowGap) {
        finalDate = DateUtils.getFinalDate();
        this.allowGap = allowGap;
    }

    @SuppressWarnings("unchecked")
    public List<TimelineEntity> sortByTimeline(List<TimelineEntity> entities) {
        List<TimelineEntity> list = new ArrayList<>();
        if (entities != null && entities.size() > 0) {
            list.addAll(entities);
            list.sort(new Comparator<TimelineEntity>() {
                @Override
                public int compare(TimelineEntity entity1, TimelineEntity entity2) {
                    Date startTime1 = entity1.getStartTime();
                    Date startTime2 = entity2.getStartTime();
                    return startTime1.compareTo(startTime2);
                }
            });
        }
        return list;
    }

    /**
     * 检查时间线是否完整，时间有序且不重叠
     *
     * @param entities
     * @return
     */
    public boolean checkTimeline(List<TimelineEntity> entities) {
        if (entities != null && entities.size() > 0) {
            for (int i = 0; i < entities.size(); i++) {
                TimelineEntity entity = entities.get(i);
                Date startTime = entity.getStartTime();
                Date endTime = entity.getEndTime();
                if (startTime == null || endTime == null) { //时间为空
                    return false;
                }
                if (startTime.getTime() >= endTime.getTime()) { //起始时间大于或等于终止时间
                    return false;
                }
                if (i == entities.size() - 1) { //最后一个
                    if (!allowGap && endTime.getTime() != finalDate.getTime()) {
                        return false;  //最后一个终止时间不是2100-01-01 00:00:00
                    }
                }
                if (i > 0) {
                    TimelineEntity lastEntity = entities.get(i - 1);
                    Date lastEndTime = lastEntity.getEndTime();
                    if (allowGap) { //允许时间线中断
                        if (lastEndTime.getTime() > startTime.getTime()) { //时间重叠
                            return false;
                        }
                    } else {
                        if (lastEndTime.getTime() != startTime.getTime()) { //时间中断或者时间重叠
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    /**
     * @param entities
     * @param newEntity
     * @param callback
     */
    public void updateTimeline(List<TimelineEntity> entities, TimelineEntity newEntity,
                               TimelineCallback callback) throws ParseException {
        if (callback == null) {
            throw new IllegalArgumentException("callback cannot be empty");
        }

        TimelineRelation relation = calcRelation(entities, newEntity);

        List<TimelineEntity> list = sortByTimeline(entities);
        List<TimelineEntity> waitForInsert = new ArrayList<>();
        List<TimelineEntity> waitForUpdate = new ArrayList<>();
        List<TimelineEntity> waitForDelete = new ArrayList<>();

        int n = list.size();
        long x = newEntity.getStartTime().getTime();
        long y = newEntity.getEndTime().getTime();

        /**
         插入逻辑，设定该段时间为[a,b)，插入时间为[x,y)
         */
        switch (relation.getRelation()) {
            case TimelineRelation.ISOLATED:
            case TimelineRelation.DISJOINT_MID:
                if (!allowGap) {
                    //不允许时间中断，终止时间为2100-01-01 00:00:00
                    newEntity.getEndTime().setTime(finalDate.getTime());
                }
                break;
            case TimelineRelation.DISJOINT_LEFT:
                if (relation.isNeedExtend()) {
                    //不允许时间中断，时间接续，新插入的终止时间向后延伸
                    newEntity.getEndTime().setTime(list.get(0).getStartTime().getTime());
                }
                break;
            case TimelineRelation.DISJOINT_RIGHT:
                if (relation.isNeedExtend()) {
                    //不允许时间中断，时间接续，最后一项的终止时间向后延伸
                    //一般不会执行到这里，防止终止时间大于2100-01-01 00:00:00
                    list.get(n - 1).getEndTime().setTime(newEntity.getStartTime().getTime());
                    waitForUpdate.add(list.get(n - 1));
                }
                break;
            case TimelineRelation.INTERSECT_COINCIDE_SEGMENT: //与一段重合   a/x-----b/y
                waitForDelete.addAll(relation.getCoveredEntities());
                break;
            case TimelineRelation.INTERSECT_INSIDE_SEGMENT: //在一段内部插入 a---x---y---b
                TimelineEntity target = relation.getIntersectedLeft();
                waitForDelete.add(target);
                TimelineEntity new1 = callback.cloneTimelineEntity(target);
                TimelineEntity new2 = callback.cloneTimelineEntity(target);
                new1.getEndTime().setTime(x);
                new2.getStartTime().setTime(y);
                waitForInsert.add(new1);
                waitForInsert.add(new2);
                break;
            case TimelineRelation.INTERSECT:  //普通相交
                TimelineEntity entityLeft = relation.getIntersectedLeft();
                TimelineEntity entityRight = relation.getIntersectedRight();
                if (entityLeft != null) {
                    entityLeft.getEndTime().setTime(x);
                    waitForUpdate.add(entityLeft);
                }
                if (entityRight != null) {
                    entityRight.getStartTime().setTime(y);
                    waitForUpdate.add(entityRight);
                }
                if (relation.getCoveredEntities().size() > 0) {
                    waitForDelete.addAll(relation.getCoveredEntities());
                }
                break;
            default:
                break;
        }
        callback.insertTimelineEntity(waitForInsert);
        if (waitForDelete.size() > 0) {
            callback.deleteTimelineEntity(waitForDelete);
        }
        if (waitForUpdate.size() > 0) {
            callback.updateTimelineEntity(waitForUpdate);
        }
    }

    public void removeEntityFromTimeline(List<TimelineEntity> entities, TimelineEntity removeEntity,
                                         TimelineCallback callback) throws ParseException {
        if (callback == null) {
            throw new IllegalArgumentException("callback cannot be empty");
        }

        // 插入算法
        List<TimelineEntity> waitForUpdate = new ArrayList<>();
        List<TimelineEntity> waitForDelete = new ArrayList<>();

        List<TimelineEntity> list = sortByTimeline(entities);
        if (!checkTimeline(list)) {
            throw new IllegalArgumentException("invalid timeline of entities");
        }
        TimelineEntity oldEntity = null;
        int oldIndex = -1;
        if (entities != null && entities.size() > 0) {
            for (int i = 0; i < entities.size(); i++) {
                TimelineEntity entity = entities.get(i);
                if (entity.getId() != null && entity.getId().equals(removeEntity.getId())) {
                    oldEntity = entity;
                    oldIndex = i;
                    break;
                }
            }
        }
        if (oldEntity == null) {
            throw new IllegalArgumentException("timeline does not contain the target entity");
        }

        if (!allowGap) { //处理更新段抽走后留下的空白
            if (oldIndex > 0) { //把前一段的结束时间后延
                long oldy = oldEntity.getEndTime().getTime();
                TimelineEntity previousEntity = list.get(oldIndex - 1);
                previousEntity.getEndTime().setTime(oldy);
                waitForUpdate.add(previousEntity);
            }
        }
        waitForDelete.add(oldEntity);
        if (waitForUpdate.size() > 0) {
            callback.updateTimelineEntity(waitForUpdate);
        }
        callback.deleteTimelineEntity(waitForDelete);

        list.remove(oldIndex);
    }

    /**
     * @param entities
     * @param updateEntity
     * @param callback
     */
    public void updateTimelineEntity(List<TimelineEntity> entities, TimelineEntity updateEntity,
                                     TimelineCallback callback) throws ParseException {
        if (callback == null) {
            throw new IllegalArgumentException("callback cannot be empty");
        }

        List<TimelineEntity> list = sortByTimeline(entities);
        if (!checkTimeline(list)) {
            throw new IllegalArgumentException("invalid timeline of entities");
        }

        // 插入算法
        List<TimelineEntity> waitForInsert = new ArrayList<>();
        List<TimelineEntity> waitForUpdate = new ArrayList<>();
        List<TimelineEntity> waitForDelete = new ArrayList<>();

        removeEntityFromTimeline(list, updateEntity, new TimelineCallback() {
            @Override
            public void insertTimelineEntity(List<TimelineEntity> entities) {
            }

            @Override
            public void updateTimelineEntity(List<TimelineEntity> entities) {
                waitForUpdate.addAll(entities);
            }

            @Override
            public void deleteTimelineEntity(List<TimelineEntity> entities) {
                waitForDelete.addAll(entities);
            }

            @Override
            public TimelineEntity cloneTimelineEntity(TimelineEntity entity) {
                return null;
            }
        });
        TimelineEntity oldEntity = waitForDelete.get(0);
        waitForDelete.remove(0);
        //此时list 已经不包含 oldEntity了，并且是有效的时间线

        long x = updateEntity.getStartTime().getTime();
        long y = updateEntity.getEndTime().getTime();
        oldEntity.getStartTime().setTime(x);
        oldEntity.getEndTime().setTime(y);
        waitForUpdate.add(oldEntity);

        updateTimeline(list, oldEntity, new TimelineCallback() {
            @Override
            public void insertTimelineEntity(List<TimelineEntity> entities) {
                if (entities.contains(oldEntity)) {
                    entities.remove(oldEntity);
                }
                waitForInsert.addAll(entities);
            }

            @Override
            public void updateTimelineEntity(List<TimelineEntity> entities) {
                for (TimelineEntity entity : entities) {
                    if (!waitForUpdate.contains(entity)) {
                        waitForUpdate.add(entity);
                    }
                }
            }

            @Override
            public void deleteTimelineEntity(List<TimelineEntity> entities) {
                waitForDelete.addAll(entities);
            }

            @Override
            public TimelineEntity cloneTimelineEntity(TimelineEntity entity) {
                return callback.cloneTimelineEntity(entity);
            }
        });

        if (waitForDelete.size() > 0) {
            callback.deleteTimelineEntity(waitForDelete);
        }
        if (waitForUpdate.size() > 0) {
            callback.insertTimelineEntity(waitForInsert);
        }
        callback.updateTimelineEntity(waitForUpdate);
    }

    /**
     * @param entities
     * @param newEntity
     */
    public TimelineRelation calcRelation(List<TimelineEntity> entities, TimelineEntity newEntity) {
        if (newEntity == null || newEntity.getStartTime() == null || newEntity.getEndTime() == null) {
            throw new IllegalArgumentException("invalid entity for update");
        }
        if (newEntity.getStartTime().getTime() > newEntity.getEndTime().getTime()) {
            throw new IllegalArgumentException("invalid entity for update");
        }

        if (entities == null || entities.size() == 0) { //这里是第一次插入记录，检查终止时间
            return new TimelineRelation(TimelineRelation.ISOLATED);
        }
        // 插入算法
        List<TimelineEntity> list = sortByTimeline(entities);
        if (!checkTimeline(list)) {
            throw new IllegalArgumentException("invalid timeline of entities");
        }
        /**
         * 算法描述
         * 时间段表示法为左闭右开区间[start,end)，即表示大于等于起始时间并小于终止时间
         * N个entity把时间划分为(N)段，为每一段编号0,1,2,....n-1
         * 第一个起始段之前记为-1段，最后一个段终止之后记为n
         *  |....(-1)....|---0---|---1---|---2---|---3---|........|---(n-1)--|....n....|
         */
        int n = list.size();
        long x = newEntity.getStartTime().getTime();
        long y = newEntity.getEndTime().getTime();
        if (y <= list.get(0).getStartTime().getTime()) { //左边界   x--y  a---b---c---d
            TimelineRelation relation = new TimelineRelation(TimelineRelation.DISJOINT_LEFT);
            if (!allowGap && y < list.get(0).getStartTime().getTime()) {
                relation.setNeedExtend(true);
            }
            return relation;
        }
        if (x >= list.get(n - 1).getEndTime().getTime()) { //右边界 a---b---c---d x--y
            TimelineRelation relation = new TimelineRelation(TimelineRelation.DISJOINT_RIGHT);
            if (!allowGap && x > list.get(n - 1).getEndTime().getTime()) {
                //不允许时间中断，时间接续，最后一项的终止时间向后延伸
                //一般不会执行到这里，防止终止时间大于2100-01-01 00:00:00
                relation.setNeedExtend(true);
            }
            return relation;
        }
        TimelineRelation relation = new TimelineRelation(TimelineRelation.INTERSECT); //相交
        //计算每一段和新插入的关系
        for (int i = 0; i < n; i++) {
            TimelineEntity entity = list.get(i);
            long a = entity.getStartTime().getTime();
            long b = entity.getEndTime().getTime();
            /**
             插入逻辑，设定该段时间为[a,b)，插入时间为[x,y)
             */
            if (y <= a || x >= b) { //当前段与插入段无关  x--y  a--b 或 a--b  x--y
                continue;
            }
            if (x == a && y == b) { //重合一段
                relation.setRelation(TimelineRelation.INTERSECT_COINCIDE_SEGMENT);
                relation.getCoveredEntities().add(entity);
                continue;
            }
            if (x > a && y < b) { //内含 a---x---y---b
                relation.setRelation(TimelineRelation.INTERSECT_INSIDE_SEGMENT);
                relation.setIntersectedLeft(entity);
                relation.setIntersectedRight(entity);
                relation.getCoveredEntities().add(entity);
                continue;
            }
            if (x <= a && y >= b) { //包含  x---a---b---y
                relation.getCoveredEntities().add(entity);
                continue;
            }
            if (x <= a && a < y && y < b) { //左交  x---a---y---b
                relation.setIntersectedRight(entity);
                continue;
            }
            if (a < x && x < b && b <= y) { // 右交  a---x---b---y
                relation.setIntersectedLeft(entity);
                continue;
            }
        }
        if (relation.getIntersectedLeft() == null
                && relation.getIntersectedRight() == null
                && relation.getCoveredEntities().size() == 0) { //在内部缝隙中
            relation.setRelation(TimelineRelation.DISJOINT_MID);
        }
        return relation;
    }
}
